// -----------------------------------------------------------------------
//  <copyright file="AutoDependencyBuilder.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/15 18:42</last-date>
// -----------------------------------------------------------------------

namespace DaprPlus.Dependency;

public class AutoDependencyBuilder(IServiceCollection services) : IAutoDependencyBuilder
{
    /// <summary>
    /// 查找并注册自动依赖注入服务
    /// </summary>
    /// <param name="context">DaprPlus上下文对象</param>
    public void Build(DaprPlusContext context)
    {
        var dependencyTypes = FindDependencyTypes(context);
        foreach (var dependencyType in dependencyTypes)
        {
            AddServices(services, dependencyType);
        }
    }

    protected Type[] FindDependencyTypes(DaprPlusContext context)
    {
        var baseTypes = new[] { typeof(ISingletonDependency), typeof(IScopeDependency), typeof(ITransientDependency) };

        bool FindTypeFunc(Type type) => type is { IsClass: true, IsAbstract: false, IsInterface: false }
            && !type.HasAttribute<IgnoreDependencyAttribute>() && (baseTypes.Any(b => b.IsAssignableFrom(type))
                || type.HasAttribute<DependencyAttribute>());
        var types = context.FindTypes(FindTypeFunc);
        return types;
    }

    protected void AddServices(IServiceCollection services, Type implementationType)
    {
        if (implementationType.IsAbstract || implementationType.IsInterface)
        {
            return;
        }

        var lifetime = GetLifetimeOrNull(implementationType);
        if (lifetime == null)
        {
            return;
        }

        var dependencyAttribute = implementationType.GetAttribute<DependencyAttribute>();
        var serviceTypes = GetImplementedInterfaces(implementationType);

        //服务类型数量为0时注册自身
        if (serviceTypes.Length == 0)
        {
            services.TryAdd(new ServiceDescriptor(implementationType, implementationType, lifetime.Value));
            return;
        }

        //服务实现显示要求注册自身时，注册自身并继续注册接口
        if (dependencyAttribute?.AddSelf == true)
        {
            services.TryAdd(new ServiceDescriptor(implementationType, implementationType, lifetime.Value));
        }

        if (serviceTypes.Length > 1)
        {
            List<string> orderTokens = new() { implementationType.Namespace.Substring(string.Empty, ".", string.Empty) };
            orderTokens.AddIfNotExist("DaprPlus");
            serviceTypes = serviceTypes.OrderByPrefixes(m => m.FullName, orderTokens.ToArray()).ToArray();
        }

        //注册服务
        for (var i = 0; i < serviceTypes.Length; i++)
        {
            var serviceType = serviceTypes[i];
            var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime.Value);
            if (lifetime.Value == ServiceLifetime.Transient)
            {
                services.TryAddEnumerable(descriptor);
                continue;
            }

            var multiple = serviceType.HasAttribute<MultipleDependencyAttribute>();
            if (i == 0)
            {
                if (multiple)
                {
                    services.Add(descriptor);
                }
                else
                {
                    AddOneService(services, descriptor, dependencyAttribute);
                }
            }
            else
            {
                if (multiple)
                {
                    services.Add(descriptor);
                }
                else
                {
                    //有多个接口，后边的接口注册使用第一个接口的实例，保证同个实现类的多个接口获得同一实例
                    var firstServiceType = serviceTypes[0];
                    descriptor = new ServiceDescriptor(serviceType, provider => provider.GetRequiredService(firstServiceType), lifetime.Value);
                    AddOneService(services, descriptor, dependencyAttribute);
                }
            }
        }
    }

    protected ServiceLifetime? GetLifetimeOrNull(Type implementationType)
    {
        var dependencyAttribute = implementationType.GetAttribute<DependencyAttribute>();
        if (dependencyAttribute != null)
        {
            return dependencyAttribute.Lifetime;
        }

        if (implementationType.IsDeriveClassFrom<ITransientDependency>())
        {
            return ServiceLifetime.Transient;
        }

        if (implementationType.IsDeriveClassFrom<IScopeDependency>())
        {
            return ServiceLifetime.Scoped;
        }

        if (implementationType.IsDeriveClassFrom<ISingletonDependency>())
        {
            return ServiceLifetime.Singleton;
        }

        return null;
    }

    protected Type[] GetImplementedInterfaces(Type implementationType)
    {
        var exceptInterfaces = new[] { typeof(IDisposable) };
        var interfaceTypes = implementationType.GetInterfaces().Where(inter =>
            !exceptInterfaces.Contains(inter) && !inter.HasAttribute<IgnoreDependencyAttribute>()).ToArray();
        for (var index = 0; index < interfaceTypes.Length; index++)
        {
            var interfaceType = interfaceTypes[index];
            if (interfaceType is { IsGenericType: true, IsGenericTypeDefinition: false, FullName: null })
            {
                interfaceTypes[index] = interfaceType.GetGenericTypeDefinition();
            }
        }

        return interfaceTypes;
    }

    protected void AddOneService(IServiceCollection services,
        ServiceDescriptor descriptor,
        DependencyAttribute? dependencyAttribute)
    {
        if (dependencyAttribute?.ReplaceExisting == true)
        {
            services.Replace(descriptor);
        }
        else if (dependencyAttribute?.TryAdd == true)
        {
            services.TryAdd(descriptor);
        }
        else
        {
            services.Add(descriptor);
        }
    }
}
